COMP3141 Software System Design and Implementation

COMP3141: Software System Design and Implementation

Term 2, 2023

Code and Notes (Week 8 Thursday (video released later))

Table of Contents

1 Live code

This is all the code I wrote during the prac walkthrough. No guarantee that it makes any sense out of context.

1.1 Haskell code

{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
module PracWk7TakeTwo where
import Control.Monad.State
import Test.QuickCheck

-- Not tail recursive
myReverse :: [a] -> [a]
myReverse [] = []
myReverse (x:xs) = myReverse xs ++ [x]

-- Tail recursive
myReverse' :: [a] -> [a]
myReverse' xs = myReverseAux xs [] where
  myReverseAux [] ys = ys
  myReverseAux (x:xs) ys = myReverseAux xs (x:ys)

{-
myModify :: (s -> s) -> State s ()
myModify f = do
  s <- get
  put (f s)

-- state monaddy version of tail recursion
myReverse'' :: [a] -> [a]
myReverse'' xs = evalState (myReverseAux xs) [] where
  myReverseAux :: [a] -> State [a] [a]
  myReverseAux []     = get -- return the state
  myReverseAux (x:xs) =
    do
      modify (x:)
      myReverseAux xs
-}


-- Monads done differently

class AltMonad m where
  returnA :: a -> m a
  joinA   :: m (m a) -> m a
  fmapA   :: (a -> b) -> m a -> m b

{-
instance AltMonad Maybe where
  -- returnA :: a -> Maybe a
  returnA = Just
  -- joinA :: Maybe(Maybe a) -> Maybe a
  joinA Nothing  = Nothing
  joinA (Just ma) = ma
  -- fmapA :: (a -> b) -> Maybe a -> Maybe b
  fmapA f Nothing = Nothing
  fmapA f (Just a) = Just (f a)

instance AltMonad [] where
  -- returnA :: a -> [a]
  returnA a = [a]
  -- joinA :: [[a]] -> [a]
  joinA   = concat
  -- fmapA :: (a -> b) -> [a] -> [b]
  fmapA   = map

instance Monad m => AltMonad m where
  returnA = return
  joinA mma = mma >>= id
  -- joinA mma = do
  --   ma <- mma
  --   x <- ma
  --   return x
  fmapA   = fmap


instance AltMonad m => Functor m where
  fmap = fmapA

instance AltMonad m => Applicative m where
  pure = returnA
  fm <*> xm = joinA $ fmapA (\x -> fmapA ($ x) fm) xm

instance AltMonad m => Monad m  where
  return = pure
  ma >>= f = joinA $ fmapA f ma

-}



-- zip lists etc

newtype MyZipList a = MyZipList [a] deriving (Eq,Show)

unZipList :: MyZipList a -> [a]
unZipList (MyZipList xs) = xs

instance Functor MyZipList where
  fmap f (MyZipList xs) = MyZipList $ fmap f xs

instance Applicative MyZipList where
  pure = MyZipList . repeat
  MyZipList fs <*> MyZipList as
    = MyZipList $ zipWith ($) fs as





-- streams

data Stream a = SCons a (Stream a)
  deriving Show

streamRepeat :: a -> Stream a
streamRepeat a = SCons a (streamRepeat a)

instance Functor Stream where
  fmap f (SCons x xs) = SCons (f x) (fmap f xs)

{- This function returns the n:th element of a stream,
   or the 0:th if n is negative. It will come
   in handy later I promise!
 -}
nth :: Stream a -> Integer -> a
nth (SCons x xs) n
  | n <= 0    = x
  | otherwise = nth xs (n-1)

{- nats is the stream of all natural numbers:

     SCons 0 (Scons 1 (Scons 2 ...))

   It should obey this property:

     n >= 0   ==>  nth nats n = n
 -}
nats :: Stream Integer
nats = SCons 0 (succ <$> nats)


instance Applicative Stream where
  pure a = SCons a (pure a)
  SCons f fs <*> SCons a as = SCons (f a) (fs <*> as)

instance Arbitrary a => Arbitrary(Stream a) where
  arbitrary = SCons <$> arbitrary <*> arbitrary

prefix :: Integer -> Stream a -> [a]
prefix n (SCons a as) | n<1 = []
                      | otherwise = a:prefix (n-1) as

streamLeftIdentityProp :: Eq a => Integer -> Stream a -> Bool
streamLeftIdentityProp n as
  = prefix n (streamRepeat id <*> as) == prefix n as

sHead :: Stream a -> a
sHead (SCons a _) = a


sKillOne :: (a -> Stream b) -> a -> Stream b
sKillOne f a = case f a of SCons _ as -> as

-- stream zip monad:
instance Monad Stream where
  return = pure
  -- (SCons a _) >>= f = f a -- first option
  -- (SCons a as) >>= f = SCons (sHead $ f a) (as >>= f) -- second option
  (SCons a as) >>= f = SCons (sHead $ f a) (as >>= sKillOne f)


{-
STREAM BIND OPTIONS

  as >>= f

  f :: a -> Stream b

as = a1 a2 a3 a4 ...

f(a1) = a1f1 a1f2 a1f2 a1f4 ..  -- this is a Stream b!

FIRST OPTION FOR BIND:
as >>= f    =     a1f1 a1f2 a1f3 a1f4 ...


RIGHT IDENTITY (broken)
  as >>= return = a1 a1 a1 a1 ... /= a1 a2 a3 a4 ...



SECOND OPTION FOR BIND:
as >>= f    =     a1f1 a2f1 a3f1 a4f1 ...

LEFT IDENTITY (broken)
return a = a a a a a a a a a
return a >>= f = af1 af1 af1 af1 ... /= f a
f a = af1 af2 af3 af4 ...


THIRD OPTION
as >>= f    =     a1f1 a2f2 a3f3 a4f4 ...

LEFT IDENTITY (held)
return a >>= f = f a

RIGHT IDENTITY (held)
as >>= return = a1 a2 a3 a4 = as

-}


zKillOne :: (a -> MyZipList b) -> a -> MyZipList b
zKillOne f a = case f a of 
  MyZipList [] -> MyZipList []
  MyZipList (b:bs) -> MyZipList bs

-- trying the same monad for MyZipList
-- THIS IS AN ILLEGAL MONAD DEFINITION
instance Monad MyZipList where
  return = pure
  MyZipList [] >>= f = MyZipList []
  MyZipList (a:as) >>= f = MyZipList $
    case f a of
      MyZipList [] -> []
      MyZipList (b:bs) -> (b : unZipList (MyZipList as >>= zKillOne f))


-- here is an example of how associativity breaks
m = MyZipList [0,1]

f 1 = MyZipList [1,0]
f _ = MyZipList [0]

g (1) = MyZipList []
g _ = MyZipList [0,0]



-- johannes's code below:
-- (throw it into a new file to test it)
{-
module ZipList where

import Test.QuickCheck
import Test.QuickCheck.Function

data MyZipList a = MyZipList [a] deriving (Eq,Show)

unZipList(MyZipList xs) = xs

instance Functor MyZipList where
  fmap f (MyZipList xs) = MyZipList $ fmap f xs

instance Applicative MyZipList where
  pure = MyZipList . repeat
  MyZipList fs <*> MyZipList xs = MyZipList $ uncurry ($) <$> zip fs xs

instance Monad MyZipList where
  return = pure
  MyZipList m >>= k = MyZipList $
    map (uncurry . flip $ (!!)) ks where
    ks = takeWhile good . zip [0..] $ map (unZipList . k) m
    good (n,[]) = False    
    good (0,xs) = True
    good (n,x:xs) = good (n-1,xs)

instance Arbitrary a => Arbitrary(MyZipList a) where
  arbitrary = MyZipList <$> arbitrary
  shrink = map MyZipList . shrink . unZipList

prop_law1 :: MyZipList Int -> Bool
prop_law1 zs =
  (zs >>= return) == zs

prop_law2 :: Int -> MyZipList Int -> Bool
prop_law2 z k =
  (return z >>= const k) == k

prop_law3 :: MyZipList Int -> (Fun Int (MyZipList Int)) -> (Fun Int (MyZipList Int))-> Bool
prop_law3 m (Fun _ k1) (Fun _ k2) =
  ((m >>= k1) >>= k2) ==
  (m >>= (\x -> k1 x >>= k2))

-- quickCheck prop_law3 found this:

m = MyZipList [0,1]

f 1 = MyZipList [-1,0]
f _ = MyZipList [0]

g (-1) = MyZipList []
g _ = MyZipList [0,0]
-}

2023-08-13 Sun 12:52

Announcements RSS